#include "main.h"

static u8 round;
#define yell() sample_play(25)

#define SHOOTWAIT 7

static void loadgun() {

	bankswitch(5, CHR_REVOLVER1);

	// Efficient load with four vertical stripes.

	addr = NTADR_A(14, 17);

	bgbuf[0] = MSB(addr) | NT_UPD_VERT;
	bgbuf[1] = LSB(addr);
	bgbuf[2] = 9;

	bgbuf[3] = 192 + 0 * 4;
	bgbuf[4] = 192 + 1 * 4;
	bgbuf[5] = 192 + 2 * 4;
	bgbuf[6] = 192 + 3 * 4;
	bgbuf[7] = 192 + 4 * 4;
	bgbuf[8] = 192 + 5 * 4;
	bgbuf[9] = 192 + 6 * 4;
	bgbuf[10] = 192 + 7 * 4;
	bgbuf[11] = 192 + 8 * 4;

	addr = NTADR_A(15, 17);

	bgbuf[12] = MSB(addr) | NT_UPD_VERT;
	bgbuf[13] = LSB(addr);
	bgbuf[14] = 9;

	bgbuf[15] = 192 + 0 * 4 + 1;
	bgbuf[16] = 192 + 1 * 4 + 1;
	bgbuf[17] = 192 + 2 * 4 + 1;
	bgbuf[18] = 192 + 3 * 4 + 1;
	bgbuf[19] = 192 + 4 * 4 + 1;
	bgbuf[20] = 192 + 5 * 4 + 1;
	bgbuf[21] = 192 + 6 * 4 + 1;
	bgbuf[22] = 192 + 7 * 4 + 1;
	bgbuf[23] = 192 + 8 * 4 + 1;

	addr = NTADR_A(16, 17);

	bgbuf[24] = MSB(addr) | NT_UPD_VERT;
	bgbuf[25] = LSB(addr);
	bgbuf[26] = 9;

	bgbuf[27] = 192 + 0 * 4 + 2;
	bgbuf[28] = 192 + 1 * 4 + 2;
	bgbuf[29] = 192 + 2 * 4 + 2;
	bgbuf[30] = 192 + 3 * 4 + 2;
	bgbuf[31] = 192 + 4 * 4 + 2;
	bgbuf[32] = 192 + 5 * 4 + 2;
	bgbuf[33] = 192 + 6 * 4 + 2;
	bgbuf[34] = 192 + 7 * 4 + 2;
	bgbuf[35] = 192 + 8 * 4 + 2;

	addr = NTADR_A(17, 17);

	bgbuf[36] = MSB(addr) | NT_UPD_VERT;
	bgbuf[37] = LSB(addr);
	bgbuf[38] = 9;

	bgbuf[39] = 192 + 0 * 4 + 3;
	bgbuf[40] = 192 + 1 * 4 + 3;
	bgbuf[41] = 192 + 2 * 4 + 3;
	bgbuf[42] = 192 + 3 * 4 + 3;
	bgbuf[43] = 192 + 4 * 4 + 3;
	bgbuf[44] = 192 + 5 * 4 + 3;
	bgbuf[45] = 192 + 6 * 4 + 3;
	bgbuf[46] = 192 + 7 * 4 + 3;
	bgbuf[47] = 192 + 8 * 4 + 3;

	if (turn) {
		bgbuf[48] = NT_UPD_EOF;
		return;
	}

	// Hide the scores
	pal_col(2, 0xf);
	pal_col(3, 0xf);

	addr = NTADR_A(4, 4);
	bgbuf[48] = MSB(addr) | NT_UPD_HORZ;
	bgbuf[49] = LSB(addr);
	bgbuf[50] = 24;

	memfill(bgbuf + 51, 0, 24);

	if (gamemode != G_DEMO) {
		bgbuf[59] = 'R' - ' ';
		bgbuf[60] = 'O' - ' ';
		bgbuf[61] = 'U' - ' ';
		bgbuf[62] = 'N' - ' ';
		bgbuf[63] = 'D' - ' ';

		bgbuf[65] = '0' + (round / 10) - ' ';
		bgbuf[66] = '0' + (round % 10) - ' ';
	}

	bgbuf[75] = NT_UPD_EOF;
}

static void cleargun() {

	addr = NTADR_A(14, 17);

	memfill(bgbuf, 0, 48);

	bgbuf[0] = MSB(addr) | NT_UPD_VERT;
	bgbuf[1] = LSB(addr);
	bgbuf[2] = 9;

	addr = NTADR_A(15, 17);

	bgbuf[12] = MSB(addr) | NT_UPD_VERT;
	bgbuf[13] = LSB(addr);
	bgbuf[14] = 9;

	addr = NTADR_A(16, 17);

	bgbuf[24] = MSB(addr) | NT_UPD_VERT;
	bgbuf[25] = LSB(addr);
	bgbuf[26] = 9;

	addr = NTADR_A(17, 17);

	bgbuf[36] = MSB(addr) | NT_UPD_VERT;
	bgbuf[37] = LSB(addr);
	bgbuf[38] = 9;

	addr = NTADR_A(12, 4);
	bgbuf[48] = MSB(addr);
	bgbuf[49] = LSB(addr);
	bgbuf[50] = 0;

	addr = NTADR_A(19, 4);
	bgbuf[51] = MSB(addr);
	bgbuf[52] = LSB(addr);
	bgbuf[53] = 0;

	bgbuf[54] = NT_UPD_EOF;
}

static void loadspin() {

	bankswitch(5, CHR_REV0);

	// Efficient load with six vertical stripes.

	addr = NTADR_A(13, 10);

	bgbuf[0] = MSB(addr) | NT_UPD_VERT;
	bgbuf[1] = LSB(addr);
	bgbuf[2] = 7;

	bgbuf[3] = 192 + 0 * 6;
	bgbuf[4] = 192 + 1 * 6;
	bgbuf[5] = 192 + 2 * 6;
	bgbuf[6] = 192 + 3 * 6;
	bgbuf[7] = 192 + 4 * 6;
	bgbuf[8] = 192 + 5 * 6;
	bgbuf[9] = 192 + 6 * 6;

	addr = NTADR_A(14, 10);

	bgbuf[10] = MSB(addr) | NT_UPD_VERT;
	bgbuf[11] = LSB(addr);
	bgbuf[12] = 7;

	bgbuf[13] = 192 + 0 * 6 + 1;
	bgbuf[14] = 192 + 1 * 6 + 1;
	bgbuf[15] = 192 + 2 * 6 + 1;
	bgbuf[16] = 192 + 3 * 6 + 1;
	bgbuf[17] = 192 + 4 * 6 + 1;
	bgbuf[18] = 192 + 5 * 6 + 1;
	bgbuf[19] = 192 + 6 * 6 + 1;

	addr = NTADR_A(15, 10);

	bgbuf[20] = MSB(addr) | NT_UPD_VERT;
	bgbuf[21] = LSB(addr);
	bgbuf[22] = 7;

	bgbuf[23] = 192 + 0 * 6 + 2;
	bgbuf[24] = 192 + 1 * 6 + 2;
	bgbuf[25] = 192 + 2 * 6 + 2;
	bgbuf[26] = 192 + 3 * 6 + 2;
	bgbuf[27] = 192 + 4 * 6 + 2;
	bgbuf[28] = 192 + 5 * 6 + 2;
	bgbuf[29] = 192 + 6 * 6 + 2;

	addr = NTADR_A(16, 10);

	bgbuf[30] = MSB(addr) | NT_UPD_VERT;
	bgbuf[31] = LSB(addr);
	bgbuf[32] = 7;

	bgbuf[33] = 192 + 0 * 6 + 3;
	bgbuf[34] = 192 + 1 * 6 + 3;
	bgbuf[35] = 192 + 2 * 6 + 3;
	bgbuf[36] = 192 + 3 * 6 + 3;
	bgbuf[37] = 192 + 4 * 6 + 3;
	bgbuf[38] = 192 + 5 * 6 + 3;
	bgbuf[39] = 192 + 6 * 6 + 3;

	addr = NTADR_A(17, 10);

	bgbuf[40] = MSB(addr) | NT_UPD_VERT;
	bgbuf[41] = LSB(addr);
	bgbuf[42] = 7;

	bgbuf[43] = 192 + 0 * 6 + 4;
	bgbuf[44] = 192 + 1 * 6 + 4;
	bgbuf[45] = 192 + 2 * 6 + 4;
	bgbuf[46] = 192 + 3 * 6 + 4;
	bgbuf[47] = 192 + 4 * 6 + 4;
	bgbuf[48] = 192 + 5 * 6 + 4;
	bgbuf[49] = 192 + 6 * 6 + 4;

	addr = NTADR_A(18, 10);

	bgbuf[50] = MSB(addr) | NT_UPD_VERT;
	bgbuf[51] = LSB(addr);
	bgbuf[52] = 7;

	bgbuf[53] = 192 + 0 * 6 + 5;
	bgbuf[54] = 192 + 1 * 6 + 5;
	bgbuf[55] = 192 + 2 * 6 + 5;
	bgbuf[56] = 192 + 3 * 6 + 5;
	bgbuf[57] = 192 + 4 * 6 + 5;
	bgbuf[58] = 192 + 5 * 6 + 5;
	bgbuf[59] = 192 + 6 * 6 + 5;

	bgbuf[60] = NT_UPD_EOF;
}

static void clearspin() {

	addr = NTADR_A(13, 10);

	memfill(bgbuf, 0, 60);

	bgbuf[0] = MSB(addr) | NT_UPD_VERT;
	bgbuf[1] = LSB(addr);
	bgbuf[2] = 7;

	addr = NTADR_A(14, 10);

	bgbuf[10] = MSB(addr) | NT_UPD_VERT;
	bgbuf[11] = LSB(addr);
	bgbuf[12] = 7;

	addr = NTADR_A(15, 10);

	bgbuf[20] = MSB(addr) | NT_UPD_VERT;
	bgbuf[21] = LSB(addr);
	bgbuf[22] = 7;

	addr = NTADR_A(16, 10);

	bgbuf[30] = MSB(addr) | NT_UPD_VERT;
	bgbuf[31] = LSB(addr);
	bgbuf[32] = 7;

	addr = NTADR_A(17, 10);

	bgbuf[40] = MSB(addr) | NT_UPD_VERT;
	bgbuf[41] = LSB(addr);
	bgbuf[42] = 7;

	addr = NTADR_A(18, 10);

	bgbuf[50] = MSB(addr) | NT_UPD_VERT;
	bgbuf[51] = LSB(addr);
	bgbuf[52] = 7;

	bgbuf[60] = NT_UPD_EOF;
}

static void demo() {

	static u8 state, spintime, aiming, spinchange;
	static u8 bulletstate, demoruns;

	demoruns = 0;
	ctrl = 0;
	state = S_SETUP_LOAD;
	turn = 0;

	// BG pal: text, left, right, gun
	// Left is walken and right deniro in the demo
	pal_col(5, walkenpal[0]);
	pal_col(6, walkenpal[1]);
	pal_col(7, walkenpal[2]);

	pal_col(9, deniropal[0]);
	pal_col(10, deniropal[1]);
	pal_col(11, deniropal[2]);

	// Images are in slots 1 and 2, 3 and 4 when offset
	bankswitch(3, CHR_WALKEN1);
	bankswitch(4, CHR_DENIRO1);

	vram_adr(NTADR_A(14, 7));
	str("DEMO");

	ppu_on_all();

	while (1) {
		oam_clear();
		oam_off = 0;
		bgbuf[0] = NT_UPD_EOF;
		ctrl = joy_read(0);
		if (ctrl & (KEY_A | KEY_START))
			break;

		musicctr++;
		if (musiclimit && musicctr > musiclimit) {
			music_stop();
			musiclimit = 0;
		}

		switch (state) {
			case S_SETUP_LOAD:
				loadgun();
				progress = 0;
				state = S_SHOW_BULLET;
			break;
			case S_SHOW_BULLET:
				if (progress < 25)
					oam_off = oam_meta_spr(10 * 8, 22 * 8, 0,
						round_metasprite);
				else
					oam_off = oam_meta_spr(11 * 8 + bulletpos[0][0],
						20 * 8 + bulletpos[0][1],
						oam_off,
						bullet_metasprite);

				if (progress > 15)
					oam_off = oam_meta_spr(11 * 8, 20 * 8, oam_off,
							chamber1_metasprite);

				progress++;
				if (progress == 25)
					sfx_play(SFX_INSERT_ROUND, 0);
				if (progress > 60) {
					progress = 0;
					state = S_LOAD;
					spinchange = 80 + (rand8() & 63);
					bulletstate = 0;
				}
			break;
			case S_LOAD:
				// Loadspin
				spinframe = progress % 4 < 2;
				if (progress % 2 == 0) {
					bulletstate++;
					bulletstate %= 6;
				}
				oam_off = oam_meta_spr(11 * 8, 20 * 8, 0,
							spinframe ? chamber2_metasprite :
							chamber1_metasprite);
				oam_off = oam_meta_spr(11 * 8 + bulletpos[spinframe][bulletstate * 2],
						20 * 8 + bulletpos[spinframe][bulletstate * 2 + 1] ,
						oam_off,
						bullet_metasprite);

				if (progress % 4 == 0 && progress < spinchange - 16)
					sfx_play(SFX_SPINNING_CHAMBER, 1);

				progress++;
				if (progress == spinchange - 15)
					sfx_play(SFX_CHAMBER_CLOSE, 0);
				if (progress > spinchange) {
					progress = 0;
					state = S_LOADED;
					bankswitch(5, CHR_REVOLVER2);
				}
			break;
			case S_LOADED:
				progress++;
				if (progress > 60) {
					progress = 0;
					state = S_SETUP_SPIN;
					if (turn)
						state = S_SHOOT;
					cleargun();
				}
			break;
			case S_SETUP_SPIN:
				loadspin();
				state = S_SPIN;
				spintime = 32 + (rand8() & 127);
				progress = 0;
				aiming = 0;
				spinprogress = 0;
				spinchange = 8 + rand8() % 32;
			break;
			case S_SPIN:
				// Spin
				spinprogress += curve[255U * progress / spintime];
				if (spinprogress > spinchange) {
					spinprogress -= spinchange;
					aiming++;
					aiming %= 8;
					bankswitch(5, CHR_REV0 + aiming);

					if (!aiming)
						sfx_play(SFX_REVOLVER_SPIN, 0);
				}

//				if (progress == 0)
//					sfx_play(SFX_REVOLVER_SPIN, 0);

				progress++;
				if (progress > spintime) {
					state = S_SPINWAIT;
					progress = 0;
				}
			break;
			case S_SPINWAIT:
				progress++;
				if (progress > 120) {
					// Which way are we?
					if (aiming == 2 || aiming == 6) {
						state = S_SETUP_SPIN;
						break;
					}

					state = S_SHOOT;
					aiming = aiming > 2 && aiming < 6; // Right is 1
					clearspin();
					progress = 0;
				}
			break;
			case S_SHOOT:
				five = progress / 18;
				if (aiming) {
					oam_off = oam_meta_spr(14 * 8 - 4, 10 * 8 + 1,
							oam_off,
							hammer_array_flip[five]);
					oam_off = oam_meta_spr(13 * 8 - 1, 11 * 8 + 1,
							oam_off,
							hand_array_flip[five]);
					oam_off = oam_meta_spr(14 * 8, 10 * 8, oam_off,
							revolver_small_metasprite_flip);
				} else {
					oam_off = oam_meta_spr(18 * 8 - 4, 10 * 8 + 1,
							oam_off,
							hammer_array[five]);
					oam_off = oam_meta_spr(16 * 8 + 1, 11 * 8 + 1,
							oam_off,
							hand_array[five]);
					oam_off = oam_meta_spr(14 * 8, 10 * 8, oam_off,
							revolver_small_metasprite);
				}

				progress++;
				if (progress >= 90) {
					sfx_play(SFX_PULL_TRIGGER, 0);
					state = S_RESULTS;
					progress = 0;
				}
			break;
			case S_RESULTS:
				// Demo always fails
				if (aiming) {
					oam_off = oam_meta_spr(14 * 8 - 4, 10 * 8 + 1,
							oam_off,
							hammer1_metasprite_flip);
					oam_off = oam_meta_spr(13 * 8 - 1, 11 * 8 + 1,
							oam_off,
							hand1_metasprite_flip);
					oam_off = oam_meta_spr(14 * 8, 10 * 8, oam_off,
							revolver_small_metasprite_flip);
				} else {
					oam_off = oam_meta_spr(18 * 8 - 4, 10 * 8 + 1,
							oam_off,
							hammer1_metasprite);
					oam_off = oam_meta_spr(16 * 8 + 1, 11 * 8 + 1,
							oam_off,
							hand1_metasprite);
					oam_off = oam_meta_spr(14 * 8, 10 * 8, oam_off,
							revolver_small_metasprite);
				}

				if (progress == 15) {
					sfx_play(SFX_CROWD_SHORT, 1);
				}

				progress++;
				if (progress > 60) {
					progress = 0;
					state = S_SETUP_LOAD;
					demoruns++;
					if (demoruns > 1) // Demo runs twice
						goto done;

					turn++;
					aiming ^= 1;
				}
			break;
		}

		ppu_wait_nmi();
	}

	done:
	ppu_off();
	delay(30);
}

static void hidechars() {
	bankswitch(3, CHR_EMPTY);
	bankswitch(4, CHR_EMPTY);
}

static void showchars(const u8 who) {
	bankswitch(3, CHR_WALKEN1);

	switch (who) {
		case C_DENIRO:
			bankswitch(4, CHR_DENIRO1);
		break;
		case C_PRISONER:
			bankswitch(4, CHR_PRISONER1);
		break;
		case C_ENT1:
			bankswitch(4, CHR_ENT1_1);
		break;
		case C_ENT2:
			bankswitch(4, CHR_ENT2_1);
		break;
	}
}

static void cpublink(const u8 who) {
	static u8 frames;
	static u8 blink;

	frames++;

	blink = frames > 10;

	switch (who) {
		case C_PRISONER:
			bankswitch(4, CHR_PRISONER0 + blink);
		break;
		case C_ENT1:
			bankswitch(4, CHR_ENT1_0 + blink);
		break;
		case C_ENT2:
			bankswitch(4, CHR_ENT2_0 + blink);
		break;
	}
}

static u16 score, score2;
static u8 state;

static void drawscores() {

	if (gamemode < G_TWO_A || state == S_SPIN || !aiming)
		pal_col(2, 0x24);
	else
		pal_col(2, 0x22);

	pal_col(3, 0x2a);

	addr = NTADR_A(5, 3);
	bgbuf[0] = MSB(addr) | NT_UPD_HORZ;
	bgbuf[1] = LSB(addr);
	bgbuf[2] = 3;

	if (state > S_SPIN && (gamemode < G_TWO_A || !aiming)) {
		bgbuf[3] = 2;
		bgbuf[4] = 3;
		bgbuf[5] = 4;
	} else {
		bgbuf[3] = 0;
		bgbuf[4] = 0;
		bgbuf[5] = 0;
	}

	addr = NTADR_A(15, 3);
	bgbuf[6] = MSB(addr) | NT_UPD_HORZ;
	bgbuf[7] = LSB(addr);
	bgbuf[8] = 2;

	bgbuf[9] = 8;
	bgbuf[10] = 9;

	addr = NTADR_A(23, 3);
	bgbuf[11] = MSB(addr) | NT_UPD_HORZ;
	bgbuf[12] = LSB(addr);
	bgbuf[13] = 3;

	if (gamemode > G_SINGLE_B && aiming && state != S_SPIN) {
		bgbuf[14] = 5;
		bgbuf[15] = 6;
		bgbuf[16] = 7;
	} else {
		bgbuf[14] = 0;
		bgbuf[15] = 0;
		bgbuf[16] = 0;
	}

	// Then the numbers
	fmtscore(score);
	addr = NTADR_A(4, 4);
	bgbuf[17] = MSB(addr) | NT_UPD_HORZ;
	bgbuf[18] = LSB(addr);
	bgbuf[19] = 6;

	bgbuf[20] = scorebuf[0] - ' ';
	bgbuf[21] = scorebuf[1] - ' ';
	bgbuf[22] = scorebuf[2] - ' ';
	bgbuf[23] = scorebuf[3] - ' ';
	bgbuf[24] = scorebuf[4] - ' ';
	bgbuf[25] = scorebuf[5] - ' ';

	fmtscore(topscore[gamemode - 1]);
	addr = NTADR_A(13, 4);
	bgbuf[26] = MSB(addr) | NT_UPD_HORZ;
	bgbuf[27] = LSB(addr);
	bgbuf[28] = 6;

	bgbuf[29] = scorebuf[0] - ' ';
	bgbuf[30] = scorebuf[1] - ' ';
	bgbuf[31] = scorebuf[2] - ' ';
	bgbuf[32] = scorebuf[3] - ' ';
	bgbuf[33] = scorebuf[4] - ' ';
	bgbuf[34] = scorebuf[5] - ' ';

	addr = NTADR_A(22, 4);
	bgbuf[35] = MSB(addr) | NT_UPD_HORZ;
	bgbuf[36] = LSB(addr);
	bgbuf[37] = 6;

	if (gamemode > G_SINGLE_B) {
		fmtscore(score2);

		bgbuf[38] = scorebuf[0] - ' ';
		bgbuf[39] = scorebuf[1] - ' ';
		bgbuf[40] = scorebuf[2] - ' ';
		bgbuf[41] = scorebuf[3] - ' ';
		bgbuf[42] = scorebuf[4] - ' ';
		bgbuf[43] = scorebuf[5] - ' ';
	} else {
		bgbuf[38] = 0;
		bgbuf[39] = 0;
		bgbuf[40] = 0;
		bgbuf[41] = 0;
		bgbuf[42] = 0;
		bgbuf[43] = 0;
	}

	bgbuf[44] = NT_UPD_EOF;
}

static void chamberpal() {
	pal_col(17, 0x30);
	pal_col(18, 0x10);
	pal_col(19, 0x0);
}

static void blastpal() {
	pal_col(17, 0x30);
	pal_col(18, 0x27);
	pal_col(19, 0x15);
}

static void slap() {
	totalstrikes[aiming]++;
	state = S_SLAPPING;
	progress = 0;
}

#define BMODE (gamemode == G_SINGLE_B || gamemode == G_TWO_B)

static void otherpal(const u8 otherchar) {
	switch (otherchar) {
		case C_DENIRO:
			pal_col(9, deniropal[0]);
			pal_col(10, deniropal[1]);
			pal_col(11, deniropal[2]);
		break;
		case C_PRISONER:
			pal_col(9, prisonerpal[0]);
			pal_col(10, prisonerpal[1]);
			pal_col(11, prisonerpal[2]);
		break;
		case C_ENT1:
			pal_col(9, ent1pal[0]);
			pal_col(10, ent1pal[1]);
			pal_col(11, ent1pal[2]);
		break;
		case C_ENT2:
			pal_col(9, ent2pal[0]);
			pal_col(10, ent2pal[1]);
			pal_col(11, ent2pal[2]);
		break;
	}
}

static void checkscore() {
	if (topscore[gamemode - 1] < score)
		topscore[gamemode - 1] = score;
	if (topscore[gamemode - 1] < score2)
		topscore[gamemode - 1] = score2;
}

static void gameover() {
	addr = NTADR_A(11, 16);
	bgbuf[0] = MSB(addr) | NT_UPD_HORZ;
	bgbuf[1] = LSB(addr);
	bgbuf[2] = 9;

	bgbuf[3] = 'G' - ' ';
	bgbuf[4] = 'A' - ' ';
	bgbuf[5] = 'M' - ' ';
	bgbuf[6] = 'E' - ' ';

	bgbuf[7] = 0;

	bgbuf[8] = 'O' - ' ';
	bgbuf[9] = 'V' - ' ';
	bgbuf[10] = 'E' - ' ';
	bgbuf[11] = 'R' - ' ';

	addr = 0x23c0 + 34;
	bgbuf[12] = MSB(addr) | NT_UPD_HORZ;
	bgbuf[13] = LSB(addr);
	bgbuf[14] = 5;

	memfill(&bgbuf[15], 0, 5);

	bgbuf[20] = NT_UPD_EOF;
}

static void drawcrosses() {
	for (i = 0; i < 3; i++) {
		if (totalstrikes[0] > i)
			oam_off = oam_spr(4 * 8 + i * 20, 14 * 8 + 2,
					0x6b, 3, oam_off);

		if (gamemode >= G_TWO_A && totalstrikes[1] > i)
			oam_off = oam_spr(22 * 8 + i * 20, 14 * 8 + 2,
					0x6b, 3, oam_off);
	}
}

void game() {

	static u8 forcedyell;

	forcedyell = 0;
	totalstrikes[0] = 0;
	totalstrikes[1] = 0;
	round = 1;
	face1 = 1;
	face2 = 1;
	score = score2 = 0;
	ctrl = ctrl2 = 0;
	prevctrl = prevctrl2 = 0;
	state = S_SETUP_LOAD;
	turn = 0;
	aiwait = aihesitate = 0;

	oam_clear();
	vram_adr(NAMETABLE_A);
	vram_fill(0, 1024);

	// BG pal: text, left, right, gun
	pal_col(0, 0xf);

	pal_col(1, 0x30);
	pal_col(2, 0x24);
	pal_col(3, 0x2a);

	pal_col(13, 0x0);
	pal_col(14, 0x10);
	pal_col(15, 0x30);

	// Sprite pal: chamber/blast, bullet, revolver, slap
	chamberpal();

	pal_col(21, 0x27);
	pal_col(22, 0x36);
	pal_col(23, 0x17);

	pal_col(25, 0x0);
	pal_col(26, 0x10);
	pal_col(27, 0x30);

	pal_col(29, 0x27);
	pal_col(30, 0x16);
	pal_col(31, 0x30);

	// Prefill the palette areas
	for (y = 17; y < 28; y++) {
		for (x = 14; x < 18; x++) {
			pal_nt(x, y, 3);
		}
	}

	for (y = 10; y < 17; y++) {
		for (x = 13; x < 19; x++) {
			pal_nt(x, y, 3);
		}
	}

	// Image setup
	i = 0;
	for (y = 0; y < 6; y++) {
		for (x = 0; x < 6; x++) {
			vram_adr(NTADR_A(4 + x, 8 + y));
			vram_put(i + 64);

			pal_nt(4 + x, 8 + y, 1);

			vram_adr(NTADR_A(22 + x, 8 + y));
			vram_put(i + 128);

			pal_nt(22 + x, 8 + y, 2);

			i++;
		}
	}

	if (gamemode == G_DEMO) {
		demo();
		return;
	}

	// Pick characters
	pal_col(5, walkenpal[0]);
	pal_col(6, walkenpal[1]);
	pal_col(7, walkenpal[2]);


	hidechars();

	otherchar = 1 + (rand8() % 3);
	if (gamemode > G_SINGLE_B)
		otherchar = C_DENIRO;
	otherpal(otherchar);

	ppu_on_all();

	while (1) {
		oam_clear();
		oam_off = 0;
		bgbuf[0] = NT_UPD_EOF;
		ctrl = joy_read(0);
		ctrl2 = joy_read(1);

		musicctr++;

		switch (state) {
			case S_SETUP_LOAD:
				if (!turn) hidechars();
				loadgun();
				progress = 0;
				state = S_SHOW_BULLET;
				cocked = 0;
				died = 0;

				strikecnt = 0;
				strikelimit = 8192 / round;
				yelled[0] = 0;
				yelled[1] = 0;

				if (gamemode < G_TWO_A) {
					aiwait = aihesitate = 0;

					if (rand8() % 2)
						aiwait = 32 + (rand8() % 60);
					if (rand8() % 2)
						aihesitate = 32 + (rand8() % 32);
				}

				if (strikelimit < 60)
					strikelimit = 60;

				if (!turn) {
					music_play(round == 1 ? MUSIC_FIRST_ROUND : MUSIC_NEXT_ROUND);
					musiclimit = round == 1 ? 900 : 480;
					musicctr = 0;
				}
			break;
			case S_SHOW_BULLET:
				if (BMODE) {
					if (progress < 75)
						oam_off = oam_meta_spr(8 * 8, 22 * 8, 0,
							round_metasprite);
					else
						oam_off = oam_meta_spr(11 * 8 + bulletpos[0][0],
							20 * 8 + bulletpos[0][1],
							oam_off,
							bullet_metasprite);

					if (progress < 50)
						oam_off = oam_meta_spr(9 * 8, 22 * 8, oam_off,
							round_metasprite);
					else
						oam_off = oam_meta_spr(11 * 8 + bulletpos[0][4],
							20 * 8 + bulletpos[0][5],
							oam_off,
							bullet_metasprite);

					if (progress < 25)
						oam_off = oam_meta_spr(10 * 8, 22 * 8, oam_off,
							round_metasprite);
					else
						oam_off = oam_meta_spr(11 * 8 + bulletpos[0][8],
							20 * 8 + bulletpos[0][9],
							oam_off,
							bullet_metasprite);


					if (progress > 15)
						oam_off = oam_meta_spr(11 * 8, 20 * 8, oam_off,
								chamber1_metasprite);
					progress++;
					if (progress == 25 || progress == 50 || progress == 75)
						sfx_play(SFX_INSERT_ROUND, 0);
					if (progress > 110)
						progress = 255;
				} else {
					if (progress < 25)
						oam_off = oam_meta_spr(10 * 8, 22 * 8, 0,
							round_metasprite);
					else
						oam_off = oam_meta_spr(11 * 8 + bulletpos[0][0],
							20 * 8 + bulletpos[0][1],
							oam_off,
							bullet_metasprite);

					if (progress > 15)
						oam_off = oam_meta_spr(11 * 8, 20 * 8, oam_off,
								chamber1_metasprite);

					progress++;
					if (progress == 25)
						sfx_play(SFX_INSERT_ROUND, 0);
					if (progress > 60)
						progress = 255;
				}

				if (progress > 254) {
					progress = 0;
					state = S_LOAD;
					spinchange = 80 + (rand8() & 63);
					bulletstate[0] = 0;
					bulletstate[1] = 2;
					bulletstate[2] = 4;
				}
			break;
			case S_LOAD:
				// Loadspin
				spinframe = progress % 4 < 2;
				if (progress % 2 == 0) {
					bulletstate[0]++;
					bulletstate[0] %= 6;

					if (BMODE) {
						bulletstate[1]++;
						bulletstate[1] %= 6;
						bulletstate[2]++;
						bulletstate[2] %= 6;
					}
				}
				oam_off = oam_meta_spr(11 * 8, 20 * 8, 0,
							spinframe ? chamber2_metasprite :
							chamber1_metasprite);
				oam_off = oam_meta_spr(11 * 8 + bulletpos[spinframe][bulletstate[0] * 2],
						20 * 8 + bulletpos[spinframe][bulletstate[0] * 2 + 1] ,
						oam_off,
						bullet_metasprite);

				if (BMODE) {
					oam_off = oam_meta_spr(11 * 8 + bulletpos[spinframe][bulletstate[1] * 2],
						20 * 8 + bulletpos[spinframe][bulletstate[1] * 2 + 1] ,
						oam_off,
						bullet_metasprite);
					oam_off = oam_meta_spr(11 * 8 + bulletpos[spinframe][bulletstate[2] * 2],
						20 * 8 + bulletpos[spinframe][bulletstate[2] * 2 + 1] ,
						oam_off,
						bullet_metasprite);
				}

				if (progress % 4 == 0 && progress < spinchange - 16)
					sfx_play(SFX_SPINNING_CHAMBER, 1);

				progress++;
				if (progress == spinchange - 15)
					sfx_play(SFX_CHAMBER_CLOSE, 0);
				if (progress > spinchange) {
					progress = 0;
					state = S_LOADED;
					bankswitch(5, CHR_REVOLVER2);
				}
			break;
			case S_LOADED:
				progress++;
				if (progress > 60) {
					progress = 0;
					eggshot = 0;
					eggfirst = 0;
					state = S_SETUP_SPIN;
					cleargun();
					showchars(otherchar);
					if (turn) {
						state = S_SHOOT;
						shoottimer = 0;
						zappershot = 0;
					}
				}
			break;
			case S_SETUP_SPIN:
				loadspin();
				state = S_SPIN;
				spintime = 32 + (rand8() & 127);
				progress = 0;
				aiming = 0;
				spinprogress = 0;
				spinchange = 8 + rand8() % 32;
			break;
			case S_SPIN:
				if (progress == 0)
					drawscores();

				if (musiclimit && musicctr > musiclimit) {
					music_stop();
					musiclimit = 0;
				}

				if (gamemode < G_TWO_A)
					cpublink(otherchar);

				// Spin
				spinprogress += curve[255U * progress / spintime];
				if (spinprogress > spinchange) {
					spinprogress -= spinchange;
					aiming++;
					aiming %= 8;
					bankswitch(5, CHR_REV0 + aiming);

					if (!aiming)
						sfx_play(SFX_REVOLVER_SPIN, 0);
				}

//				if (progress == 0)
//					sfx_play(SFX_REVOLVER_SPIN, 0);

				progress++;
				if (progress > spintime) {
					state = S_SPINWAIT;
					progress = 0;
				}
			break;
			case S_SPINWAIT:
				progress++;

				if (gamemode < G_TWO_A)
					cpublink(otherchar);

				if (progress > 120) {
					// Which way are we?
					if (aiming == 2 || aiming == 6) {
						state = S_SETUP_SPIN;
						break;
					}

					state = S_SHOOT;
					shoottimer = 0;
					aiming = aiming > 2 && aiming < 6; // Right is 1
					clearspin();
					progress = 0;
					zappershot = 0;
				}
			break;
			case S_SHOOT:
				if (progress == 0)
					drawscores();

				if (shoottimer < SHOOTWAIT)
					shoottimer++;

				if (musiclimit && musicctr > musiclimit) {
					music_stop();
					musiclimit = 0;
				}

				five = progress / 18;
				if (aiming) {
					if (cocked) {
						oam_off = oam_meta_spr(14 * 8 - 4, 10 * 8 + 1,
								oam_off,
								hammer_array_flip[4]);

						if (shoottimer >= SHOOTWAIT) {
							if (cocked < 15) {
								cocked++;

								oam_off = oam_meta_spr(13 * 8 - 1, 10 * 8 + 1,
										oam_off,
										hand_cocking_metasprite_flip);
							} else {
								oam_off = oam_meta_spr(13 * 8 - 1, 11 * 8 + 1,
										oam_off,
										hand_array_flip[0]);
							}
						}
					} else {
						oam_off = oam_meta_spr(14 * 8 - 4, 10 * 8 + 1,
								oam_off,
								hammer_array_flip[five]);
						if (shoottimer >= SHOOTWAIT)
							oam_off = oam_meta_spr(13 * 8 - 1, 11 * 8 + 1,
									oam_off,
									hand_array_flip[five]);
					}
					oam_off = oam_meta_spr(14 * 8, 10 * 8, oam_off,
							revolver_small_metasprite_flip);

					if (shoottimer >= SHOOTWAIT) {
						if (zapper && gamemode > G_SINGLE_B) {
							if (zappershot) {
								progress = 90;
								zappershot = 0;
							} else if (progress) {
								progress--;
							}
						} else {
							if (ctrl2 & KEY_A && gamemode > G_SINGLE_B) {
								progress++;
							} else if (ctrl2 & KEY_B && !cocked && gamemode > G_SINGLE_B) {
								cocked = 1;
								sfx_play(SFX_CHAMBER_CLOSE, 0);
								progress = 89;
							} else if (!cocked && progress && gamemode > G_SINGLE_B) {
								--progress;
							} else if (progress && gamemode < G_TWO_A) {
								if (aihesitate && progress > 45) {
									--aihesitate;
									--progress;
								}
							}
						}
					} else {
						zappershot = 0;
					}
				} else {
					if (cocked) {
						oam_off = oam_meta_spr(18 * 8 - 4, 10 * 8 + 1,
								oam_off,
								hammer_array[4]);

						if (shoottimer >= SHOOTWAIT) {
							if (cocked < 15) {
								cocked++;
								oam_off = oam_meta_spr(16 * 8 + 1, 10 * 8 + 1,
										oam_off,
										hand_cocking_metasprite);
							} else {
								oam_off = oam_meta_spr(16 * 8 + 1, 11 * 8 + 1,
										oam_off,
										hand_array[0]);
							}
						}
					} else {
						oam_off = oam_meta_spr(18 * 8 - 4, 10 * 8 + 1,
								oam_off,
								hammer_array[five]);
						if (shoottimer >= SHOOTWAIT)
							oam_off = oam_meta_spr(16 * 8 + 1, 11 * 8 + 1,
									oam_off,
									hand_array[five]);
					}
					oam_off = oam_meta_spr(14 * 8, 10 * 8, oam_off,
							revolver_small_metasprite);

					if (shoottimer >= SHOOTWAIT) {
						if (zapper) {
							if (zappershot) {
								progress = 90;
								zappershot = 0;
							} else if (progress) {
								progress--;
							}
						} else {
							if (ctrl & KEY_A) {
								progress++;
							} else if (ctrl & KEY_B && !cocked) {
								cocked = 1;
								sfx_play(SFX_CHAMBER_CLOSE, 0);
								progress = 89;
							} else if (!cocked && progress) {
								progress--;
							}
						}
					} else {
						zappershot = 0;
					}
				}

				if (gamemode < G_TWO_A)
					cpublink(otherchar);

				if (aiming && gamemode < G_TWO_A) {
					if (aiwait)
						--aiwait;
					else
						++progress;
				}

				if (eggfirst)
					progress = 90;

				if (!aiming || gamemode > G_SINGLE_B)
					strikecnt++;

				// Easter egg: in single-player modes, the second ctrl's start yells
				if (gamemode < G_TWO_A && !zapper && (ctrl2 & KEY_START) &&
					!aiming && !(prevctrl2 & KEY_START))
					forcedyell = 1;

				if (forcedyell ||
					(strikecnt > strikelimit && (!aiming || gamemode > G_SINGLE_B))) {
					// They waited too long; yell or slap

					if (round == 1 && !forcedyell) {
						// Nothing.
					} else if (round < 10) {
						if (yelled[aiming] < 3) {
							yell();
							yelled[aiming]++;
						} else {
							slap();
						}
					} else if (round < 18) {
						if (yelled[aiming] < 2) {
							yell();
							yelled[aiming]++;
						} else {
							slap();
						}
					} else {
						if (yelled[aiming] < 1) {
							yell();
							yelled[aiming]++;
						} else {
							slap();
						}
					}

					forcedyell = 0;
					strikecnt = 0;
				}

				if (progress >= 90) {
					state = S_RESULTS;
					progress = 0;

					if (!aiming && ctrl & KEY_RIGHT && !zapper)
						eggshot = 1;
					if (aiming && gamemode > G_SINGLE_B &&
						ctrl2 & KEY_LEFT && !zapper)
						eggshot = 1;

					if (eggshot && !eggfirst)
						aiming ^= 1;

					// Did we die?
					if (gamemode == G_SINGLE_A || gamemode == G_TWO_A) {
						died = (rand8() % 6) == 0;
					} else {
						died = rand8() % 2;
					}

					if (died) {
						sfx_play(SFX_SHOT, 0);
						blastpal();
						whichdeath = rand8() % 2;

						// Scoring
						switch (gamemode) {
							case G_SINGLE_A:
								if (aiming)
									score += 5;
							break;
							case G_SINGLE_B:
								if (aiming)
									score += 15;
							break;
							case G_TWO_A:
								if (aiming)
									score += 5;
								else
									score2 += 5;
							break;
							case G_TWO_B:
								if (aiming) {
									if (!score)
										score += 15;
									else
										score *= 2;
								} else {
									if (!score2)
										score2 += 15;
									else
										score2 *= 2;
								}
							break;
						}
					} else if (eggshot) {
						// no round in the chamber, the player
						// attempting to pull off this easter egg
						// gets a strike (on Mode B, it counts as
						// three strikes and an immediate 'Game Over')

						if (!eggfirst) {
							sfx_play(SFX_PULL_TRIGGER, 0);
							progress = 0;
							eggfirst = 1;
							cocked = 0;
							state = S_SHOOT;
						} else {
							drawcrosses();
							delay(60);
							state = S_SLAPPING;

							if (BMODE) {
								if (aiming)
									totalstrikes[0] += 3;
								else
									totalstrikes[1] += 3;
							} else {
								if (aiming)
									totalstrikes[0]++;
								else
									totalstrikes[1]++;
							}

							aiming ^= 1;
						}
					} else {
						sfx_play(SFX_PULL_TRIGGER, 0);
						// Scoring
						switch (gamemode) {
							case G_SINGLE_A:
								if (!aiming)
									score += 20;
							break;
							case G_SINGLE_B:
								if (!aiming) {
									if (!score)
										score += 60;
									else
										score *= 2;
								}
							break;
							case G_TWO_A:
								if (!aiming) {
									score += 20;
								} else {
									score2 += 20;
								}
							break;
							case G_TWO_B:
								if (!aiming) {
									if (!score)
										score += 60;
									else
										score *= 2;
								} else {
									if (!score2)
										score2 += 60;
									else
										score2 *= 2;
								}
							break;
						}
					}

					checkscore();
					drawscores();
				}
			break;
			case S_RESULTS:
				// Did we die?
				if (aiming) {
					if (died || !turn || progress < 73) {
						oam_off = oam_meta_spr(14 * 8 - 4, 10 * 8 + 1,
								oam_off,
								hammer1_metasprite_flip);
						oam_off = oam_meta_spr(13 * 8 - 1, 11 * 8 + 1,
								oam_off,
								progress < 10 ?
								hand5_metasprite_flip :
								hand1_metasprite_flip);
						oam_off = oam_meta_spr(14 * 8, 10 * 8, oam_off,
								revolver_small_metasprite_flip);
					}

					if (died && progress < 10) {
						x = progress * 5;
						oam_off = oam_meta_spr(18 * 8 + 4 + x, 10 * 8 - 5,
								oam_off,
								progress < 5 ?
								blast1_metasprite_flip :
								blast2_metasprite_flip);
					}
				} else {
					if (died || !turn || progress < 73) {
						oam_off = oam_meta_spr(18 * 8 - 4, 10 * 8 + 1,
								oam_off,
								hammer1_metasprite);
						oam_off = oam_meta_spr(16 * 8 + 1, 11 * 8 + 1,
								oam_off,
								progress < 10 ?
								hand5_metasprite :
								hand1_metasprite);
						oam_off = oam_meta_spr(14 * 8, 10 * 8, oam_off,
								revolver_small_metasprite);
					}

					if (died && progress < 10) {
						x = progress * 5;
						oam_off = oam_meta_spr(13 * 8 - x, 10 * 8 - 5,
								oam_off,
								progress < 5 ?
								blast1_metasprite :
								blast2_metasprite);
					}
				}

				// Death animation
				if (died && progress < 25) {
					if (aiming) {
						x = (numdeath[otherchar][whichdeath] *
							(u16) progress) / 25;
						y = deathpos[otherchar][whichdeath] + x;

						// The empty frame is not duplicated
						if (whichdeath &&
							x == numdeath[otherchar][whichdeath] - 1)
							y = deathpos[otherchar][0] +
								numdeath[otherchar][0] - 1;

						switch (otherchar) {
							case C_DENIRO:
								bankswitch(4, CHR_DENIRO0 + y);
							break;
							case C_PRISONER:
								bankswitch(4, CHR_PRISONER0 + y);
							break;
							case C_ENT1:
								bankswitch(4, CHR_ENT1_0 + y);
							break;
							case C_ENT2:
								bankswitch(4, CHR_ENT2_0 + y);
							break;
						}
					} else {
						x = (numdeath[4][whichdeath] *
							(u16) progress) / 25;
						y = deathpos[4][whichdeath] + x;

						// The empty frame is not duplicated
						if (whichdeath &&
							x == numdeath[4][whichdeath] - 1)
							y = deathpos[4][0] +
								numdeath[4][0] - 1;

						bankswitch(3, CHR_WALKEN0 + y);
					}
				}

				if (progress == 60 && !died && turn)
					music_play(MUSIC_ROUND_END);

				if (progress == 15 && !died) {
					if (round > 15 || BMODE)
						sfx_play(SFX_CROWD_LONG, 1);
					else if (round > 5)
						sfx_play(SFX_CROWD_MEDIUM, 1);
					else
						sfx_play(SFX_CROWD_SHORT, 1);
				}

				progress++;
				if ((turn && progress >= 180) ||
					(!turn && progress >= 60)) {
					progress = 0;
					state = S_SETUP_LOAD;
					chamberpal();

					if (died && gamemode > G_SINGLE_B) {
						music_play(MUSIC_GAMEOVER);
						gameover();
						delay(150);
						bgbuf[0] = NT_UPD_EOF;
						delay(130);
						goto done;
					}
					if (died && !aiming && gamemode < G_TWO_A) {
						music_play(MUSIC_GAMEOVER);
						gameover();
						delay(150);
						bgbuf[0] = NT_UPD_EOF;
						delay(130);
						goto done;
					}
					if (died && aiming && gamemode < G_TWO_A) {
						// cycle players
						otherchar++;
						if (otherchar >= C_WALKEN)
							otherchar = C_PRISONER;
						otherpal(otherchar);
					}

					turn++;
					aiming ^= 1;

					if (eggshot) {
						aiming ^= 1;
						eggshot = 0;
					}

					if (turn >= 2) {
						turn = 0;
						round++;
					}
				}
			break;
			case S_SLAPPING:
				if (progress == 15) {
					if (aiming)
						bankswitch(4, CHR_DENIRO0 + 2);
					else
						bankswitch(3, CHR_WALKEN0 + 2);
				} else if (progress == 45) {
					if (aiming)
						bankswitch(4, CHR_DENIRO0 + 3);
					else
						bankswitch(3, CHR_WALKEN0 + 3);
				}

				if (progress < 15) {
					x = progress * 3;
					if (aiming) {
						oam_off = oam_meta_spr(21 * 8 + x, 10 * 8 + 1, 0,
							slaphand_metasprite_flip);
					} else {
						oam_off = oam_meta_spr(10 * 8 - x, 10 * 8 + 1, 0,
							slaphand_metasprite);
					}
				}

				if (progress == 5) {
					if (rand8() % 2)
						sfx_play(SFX_SLAP1, 0);
					else
						sfx_play(SFX_SLAP2, 0);
				}

				progress++;
				if (progress > 60) {
					progress = 0;
					state = S_RESULTS;

					if (totalstrikes[aiming] >= 3) {
						sfx_play(SFX_PENALTY, 1);
						music_play(MUSIC_GAMEOVER2);
						gameover();
						oam_off = oam_meta_spr(104, 90 - 16,
								oam_off, strike_metasprite);
						delay(100);
						bgbuf[0] = NT_UPD_EOF;
						delay(200);
						delay(30);
						goto done;
					}

					if (eggshot)
						aiming ^= 1;
				}
			break;
		}

		// Faces
		switch (state) {
			case S_SPIN:
			case S_SPINWAIT:
			case S_SHOOT:
			case S_RESULTS:
			if (state != S_RESULTS || !died) {
				if (zapper) {
					if (ctrl & KEY_UP && !(prevctrl & KEY_UP)) {
						if (aiming || gamemode < G_TWO_A) {
							if (face1)
								face1--;
							else
								face1 = 17;

							bankswitch(3, CHR_WALKEN0 + face1);
						} else if (!aiming && gamemode > G_SINGLE_B) {
							if (face2)
								face2--;
							else
								face2 = 17;

							bankswitch(4, CHR_DENIRO0 + face2);
						}
					} else if (ctrl & KEY_DOWN && !(prevctrl & KEY_DOWN)) {
						if (aiming || gamemode < G_TWO_A) {
							face1++;
							face1 %= 18;

							bankswitch(3, CHR_WALKEN0 + face1);
						} else if (!aiming && gamemode > G_SINGLE_B) {
							face2++;
							face2 %= 18;

							bankswitch(4, CHR_DENIRO0 + face2);
						}
					}
				} else {
					if (ctrl & KEY_UP && !(prevctrl & KEY_UP)) {
						if (face1)
							face1--;
						else
							face1 = 17;

						bankswitch(3, CHR_WALKEN0 + face1);
					} else if (ctrl & KEY_DOWN && !(prevctrl & KEY_DOWN)) {
						face1++;
						face1 %= 18;

						bankswitch(3, CHR_WALKEN0 + face1);
					}

					if (gamemode > G_SINGLE_B) {
						if (ctrl2 & KEY_UP && !(prevctrl2 & KEY_UP)) {
							if (face2)
								face2--;
							else
								face2 = 17;

							bankswitch(4, CHR_DENIRO0 + face2);
						} else if (ctrl2 & KEY_DOWN && !(prevctrl2 & KEY_DOWN)) {
							face2++;
							face2 %= 18;

							bankswitch(4, CHR_DENIRO0 + face2);
						}
					}
				}
			break;
			}
		}

		// Strike counts
		{
			if (state < S_SPIN && !turn)
				goto nostrike;
			drawcrosses();

		}
		nostrike:

		if (zapper)
			ppu_wait_zapper();
		else
			ppu_wait_nmi();

		prevctrl = ctrl;
		prevctrl2 = ctrl2;
	}

	done:
	music_stop();
	ppu_off();
	delay(30);

	checkscore();
}
